Return to main page

Robot Sensor Example Applications

Click on the example application names below to look at the source code:

  1. whisker.c - Debounced Switch Input
  2. irdetect.c - IR LED and TV Remote Control Based Object Sensors
  3. remote.c - "Sony" TV Remote Control Receiver
  4. combine.c - Combining IR Object Detection and TV Remote Control Receiver Functions
  5. ultra.c - Ultrasonic Object Detection and Ranging
  6. light1.c - Using the PIC16F627 Voltage Comparator to Detect Differing Light Levels
  7. light2.c - Measuring the resistance of a CDS Cell
  8. sound.c - Listening for commands

whisker.c - Debounced Switch Input

The code presented in the \code\whisker\whisker.c file on the CD-ROM is designed to be used as basis for the mechalogic code which senses when the robot collides with an object.
#include 

//  02.04.19 - WHISKER: Debounce Pulled Up Switch Input.
//
//  This is a two wire interface using a 74LS174 as a shift
//   register and the code called from the mainline (and timed 
//   from TMR0 interrupt)
//
//
//  Hardware Notes:
//  PIC16F827 Running at 4 MHz with External Oscillator
//  RB0 - Pulled Up Switch Input (NOT Using Internal PORTB Pull Ups)
//  RB1 - CLock Signal to the LCD
//  RB2 - Data Signal to the LCD
//  

//  Global Variables
int RTC = 0;					//  Real Time Clock Counter

volatile char LCDDlay = 20;			//  Initialization Delay Value
volatile char LCDState = 1;			//  Current LCD State

static volatile bit Clock     @ (unsigned)&PORTB*8+1;
static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1;
static volatile bit Data      @ (unsigned)&PORTB*8+2;
static volatile bit DataTRIS  @ (unsigned)&TRISB*8+2;

char * MessageOut;				//  Message to be Sent Out
volatile char MessageOuti = 0;		//  Index to current Message Byte
char Message1[2] = "\f";			//  Clear Display
char Message2[12] = "\fdebouncing";		//  Display "debouncing"
char Message3[9] = "\fPRESSED";		//  Display "PRESSED"

volatile char ButtonPress = 0;		//  Count of the Button Presses


//  Configuration Fuses
#if defined (_16F84)
#warning PIC16F84 selected
	__CONFIG(0x03FF1);		//  PIC16F84 Configuration Fuses:
						//   - XT Oscillator
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
#elif defined(_16F627)
#warning PIC16F627 with external XT oscillator selected
	__CONFIG(0x03F61);		//  PIC116F627 Configuration Fuses:
						//   - External "XT" Oscillator
						//   - RA6/RA7 Digital I/O
						//   - External Reset
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
						//   - BODEN Enabled
#else
#error Unsupported PICmicro MCU selected
#endif


//  Subroutines
LCDNybble(char Nybble, char RS) { 		//  Send Nybble to LCD

unsigned int i; 

	Data = 0; // Clear the '174 
	for (i = 0; i < 6; i++) { 		// Repeat for six bits 
		Clock = 1; Clock = 0; 		// Write the "0"s into the '174 
	}   //  endfor 

	Data = 1; 				// Output the "AND" Value 
	Clock = 1; Clock = 0; 

	Data = RS; 				// Output the RS Bit Value 
	Clock = 1; Clock = 0; 

	for (i = 0; i < 4; i++) { 		// Output the Nybble 
		if ((Nybble & 0x008) != 0) 
			Data = 1; 		// Output the High Order Bit 
		else 
			Data = 0; 
		Clock = 1; Clock = 0; 		// Strobe the Clock 
		Nybble = Nybble << 1; 		// Shift up Nybble for Next Byte 
	}  //  endfor

	Data = 1; Data = 0; 			// Toggle the "E" Clock Bit 

}  //  End LCDNybble 

LCDByte(char Byte, char RS) {	 		//  Send Byteto LCD

	LCDNybble((Byte >> 4) & 0x00F, RS);	//  Send High Nybble
	LCDNybble(Byte & 0x00F, RS);		//  Send Low Nybble

}  //  End LCDByte


LCDInit()					//  Initialize the LCD I/O Pins
{

	Clock = 0; Data = 0;			//  Low I/O Bits
	ClockTRIS = 0; DataTRIS = 0;

}  //  End LCDInit


LCDOut(char * const LCDString)	//  Output the Data String
{

	while (LCDState);		//  Wait for LCD Available

	MessageOut = LCDString;		//  Load up the string
	LCDState = 100;			//  Start Sending the String

}  //  End LCDOut


//  Interrupt Handler
void interrupt tmr0_int(void)			//  TMR0 Interrupt Handler
{

char temp;

	if (T0IF) {

		T0IF = 0;			//  Reset Interrupt Flag

		RTC++;				//  Increment the Clock

//  Put additional interface code for 1 msec interrupt here

//  Increment Counter if Button Pressed

		if (!RB0) 			//  Button Pressed
			if (ButtonPress < 20)	//  Button Press being debounced
				ButtonPress++;
			else;			//  Button Pressed for 20 msecs
		else				//  Button Released
			if (ButtonPress != 0)	//  Button Release being debounced
				ButtonPress--;
			else;			//  Button released for more than 20 msecs

//  LCD State Machine

		switch(LCDState) {			//  Process the State Machine information
			case 1:				//  Start LCD Initialization Process
				if (--LCDDlay == 0)
					LCDState++;
				break;			//  Wait 20 msecs for LCD to reset
			case 2:				//
				LCDNybble(0x003, 0);	//  Step 2 - Send Init Char
				LCDDlay = 5;
				LCDState++;
			case 3:				//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 4:
				LCDNybble(0x003, 0);	//  Step 3 - Send Init Char
				LCDState++;
				break;
			case 5:
				LCDNybble(0x003, 0);	//  Step 4 - Send Init Char
				LCDState++;
				break;
			case 6:
				LCDNybble(0x002, 0);	//  Step 5 - Set Operating
				LCDState++;		//   Interface Size (4 Bits)
				break;
			case 7:
				LCDByte(0x028, 0);	//  Step 6 - Set Operating
				LCDState++;			
				break;
			case 8:
				LCDByte(0x008, 0);	//  Step 7 - Display Off
				LCDState++;			
				break;
			case 9:
				LCDByte(0x001, 0);	//  Step 8 - Clear Display
				LCDState++;			
				LCDDlay = 5;		//  Wait 5 msecs for Clear to Complete
				break;
			case 10:			//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 11:
				LCDByte(0x006, 0);	//  Step 9 - Shift 
				LCDState++;			
				break;
			case 12:
				LCDByte(0x00E, 0);	//  Step 10 - Display On
				LCDState = 0;		//  Ready to Run
				break;

			case 100:				//  Output a Character in "MessageOut"
				switch (temp = MessageOut[MessageOuti++]) {
					case '\0':		//  At the End of the Message
						LCDState = 0;
						MessageOuti = 0;	//  Reset Message Index
						break;
					case '\f':		//  Clear Display
						LCDByte(0x001, 0);
						LCDState++;
						LCDDlay = 5;
						break;
					case 254:		//  Command Byte follows
						if ((temp = MessageOut[MessageOuti++]) == 0)
							LCDState = 0;
						else {
							if (temp < 4) {
								LCDState++;
								LCDDlay = 5;
							}  //  endif
							LCDByte(temp, 0);
						}  //  endif
						break;
					default:		//  All other characters
						LCDByte(temp, 1);
				}  //  endswitch
				break;
			case 101:				//  Message Delay
				if (--LCDDlay == 0)
					LCDState--;
				break;
		}  //  endswitch

	}  //  endif

//  Put Different Interrupt Handlers here

}  //  End Interrupt Handler


//  Mainline
void main(void)					//  Template Mainline
{

	OPTION = 0x0D1;				//  Assign Prescaler to TMR0
						//   Prescaler is /4
	TMR0 = 0;				//  Reset the Timer for Start
	T0IE = 1;				//  Enable Timer Interrupts
	GIE = 1;				//  Enable Interrupts

	LCDInit();				//  Initialize the LCD Port

//  Put in Interface initialization code here

	while (1 == 1) {			//  Loop forever

//  Put in Robot high level operation code here

		switch(ButtonPress) {	//  Process Button Press
			case 0:		//  Released - Fully Debounced
				LCDOut(Message1);
				break;
			case 20:		//  Pressed - Fully Debounced
				LCDOut(Message3);
				break;
			default:		//  Between 0 and 20 - Button being debounced
				LCDOut(Message2);
				break;
		}  //  endswitch

	}  //  endwhile

}  //  End of Mainline
    

irdetect.c - IR LED and TV Remote Control Based Object Sensors

Infra-red object detection may seem to be somewhat esoteric for use with simple hobby robots, but they actually work very well and provide the advantage over whiskers that objects can be detected before the robot collides with them and is potentially damaged.

The source code \code\irdetect\irdetect.c file on the CD-ROM is:

#include 

//  02.04.17 - IRDETECT: Sense Objects in front of IR LED/IR Detector
//
//  This is a two wire interface using a 74LS174 as a shift
//   register and the code called from the mainline (and timed 
//   from TMR0 interrupt)
//
//
//  Hardware Notes:
//  PIC16F827 Running at 4 MHz with External Oscillator
//  RB0 - IR Detector Input
//  RB1 - CLock Signal to the LCD
//  RB2 - Data Signal to the LCD
//  RB3 - PWM Output
//  

//  Global Variables
int RTC = 0;					//  Real Time Clock Counter

volatile char LCDDlay = 20;			//  Initialization Delay Value
volatile char LCDState = 1;			//  Current LCD State

static volatile bit Clock     @ (unsigned)&PORTB*8+1;
static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1;
static volatile bit Data      @ (unsigned)&PORTB*8+2;
static volatile bit DataTRIS  @ (unsigned)&TRISB*8+2;

char * MessageOut;				//  Message to be Sent Out
volatile char MessageOuti = 0;		//  Index to current Message Byte
char Message1[2] = "\f";			//  Clear Display
char Message2[11] = "\fCOLLISION";		//  Display "Collision"

volatile char Collision = 0;			//  Current Collision
volatile char OldCollision = 0;


//  Configuration Fuses
#if defined(_16F627)
#warning PIC16F627 with external XT oscillator selected
	__CONFIG(0x03F61);		//  PIC116F627 Configuration Fuses:
						//   - External "XT" Oscillator
						//   - RA6/RA7 Digital I/O
						//   - External Reset
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
						//   - BODEN Enabled
#else
#error Unsupported PICmicro MCU selected
#endif


//  Subroutines
LCDNybble(char Nybble, char RS) { 		//  Send Nybble to LCD

unsigned int i; 

	Data = 0; // Clear the '174 
	for (i = 0; i < 6; i++) { 		// Repeat for six bits 
		Clock = 1; Clock = 0; 		// Write the "0"s into the '174 
	}   //  endfor 

	Data = 1; 				// Output the "AND" Value 
	Clock = 1; Clock = 0; 

	Data = RS; 				// Output the RS Bit Value 
	Clock = 1; Clock = 0; 

	for (i = 0; i < 4; i++) { 		// Output the Nybble 
		if ((Nybble & 0x008) != 0) 
			Data = 1; 		// Output the High Order Bit 
		else 
			Data = 0; 
		Clock = 1; Clock = 0; 		// Strobe the Clock 
		Nybble = Nybble << 1; 		// Shift up Nybble for Next Byte 
	}  //  endfor

	Data = 1; Data = 0; 			// Toggle the "E" Clock Bit 

}  //  End LCDNybble 

LCDByte(char Byte, char RS) {	 		//  Send Byteto LCD

	LCDNybble((Byte >> 4) & 0x00F, RS);	//  Send High Nybble
	LCDNybble(Byte & 0x00F, RS);		//  Send Low Nybble

}  //  End LCDByte


LCDInit()					//  Initialize the LCD I/O Pins
{

	Clock = 0; Data = 0;			//  Low I/O Bits
	ClockTRIS = 0; DataTRIS = 0;

}  //  End LCDInit


LCDOut(char * const LCDString)	//  Output the Data String
{

	while (LCDState);		//  Wait for LCD Available

	MessageOut = LCDString;		//  Load up the string
	LCDState = 100;			//  Start Sending the String

}  //  End LCDOut


//  Interrupt Handler
void interrupt tmr0_int(void)			//  TMR0 Interrupt Handler
{

char temp;

	if (T0IF) {

		T0IF = 0;			//  Reset Interrupt Flag

		RTC++;				//  Increment the Clock

//  Put additional interface code for 1 msec interrupt here

//  Output a Series of Pulses for Collision Detection

		TRISB3 = 0;			//  Enable PWM Output

		while (TMR0 < 64);		//  For a Limited Number of Cycles

		if (!RB0)			//  If Low, then Collision
			Collision = 1;
		else
			Collision = 0;

		TRISB3 = 1;

//  LCD State Machine

		switch(LCDState) {			//  Process the State Machine information
			case 1:				//  Start LCD Initialization Process
				if (--LCDDlay == 0)
					LCDState++;
				break;			//  Wait 20 msecs for LCD to reset
			case 2:				//
				LCDNybble(0x003, 0);	//  Step 2 - Send Init Char
				LCDDlay = 5;
				LCDState++;
			case 3:				//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 4:
				LCDNybble(0x003, 0);	//  Step 3 - Send Init Char
				LCDState++;
				break;
			case 5:
				LCDNybble(0x003, 0);	//  Step 4 - Send Init Char
				LCDState++;
				break;
			case 6:
				LCDNybble(0x002, 0);	//  Step 5 - Set Operating
				LCDState++;		//   Interface Size (4 Bits)
				break;
			case 7:
				LCDByte(0x028, 0);	//  Step 6 - Set Operating
				LCDState++;			
				break;
			case 8:
				LCDByte(0x008, 0);	//  Step 7 - Display Off
				LCDState++;			
				break;
			case 9:
				LCDByte(0x001, 0);	//  Step 8 - Clear Display
				LCDState++;			
				LCDDlay = 5;		//  Wait 5 msecs for Clear to Complete
				break;
			case 10:			//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 11:
				LCDByte(0x006, 0);	//  Step 9 - Shift 
				LCDState++;			
				break;
			case 12:
				LCDByte(0x00E, 0);	//  Step 10 - Display On
				LCDState = 0;		//  Ready to Run
				break;

			case 100:				//  Output a Character in "MessageOut"
				switch (temp = MessageOut[MessageOuti++]) {
					case '\0':		//  At the End of the Message
						LCDState = 0;
						MessageOuti = 0;	//  Reset Message Index
						break;
					case '\f':		//  Clear Display
						LCDByte(0x001, 0);
						LCDState++;
						LCDDlay = 5;
						break;
					case 254:		//  Command Byte follows
						if ((temp = MessageOut[MessageOuti++]) == 0)
							LCDState = 0;
						else {
							if (temp < 4) {
								LCDState++;
								LCDDlay = 5;
							}  //  endif
							LCDByte(temp, 0);
						}  //  endif
						break;
					default:		//  All other characters
						LCDByte(temp, 1);
				}  //  endswitch
				break;
			case 101:				//  Message Delay
				if (--LCDDlay == 0)
					LCDState--;
				break;
		}  //  endswitch

	}  //  endif

//  Put Different Interrupt Handlers here

}  //  End Interrupt Handler


//  Mainline
void main(void)					//  Template Mainline
{

	OPTION = 0x0D1;				//  Assign Prescaler to TMR0
						//   Prescaler is /4
	TMR0 = 0;				//  Reset the Timer for Start
	T0IE = 1;				//  Enable Timer Interrupts
	GIE = 1;				//  Enable Interrupts

	LCDInit();				//  Initialize the LCD Port

//  Put in Interface initialization code here

	CCPR1L = 13;				//  50% Duty Cycle
	CCP1CON = 0b000001111;			//  Turn on PWM Mode

	PR2 = 26;				//  26 usec Cycle for 38 KHz
	TMR2 = 0;				//  Reset TMR2
	T2CON = 0b000000100;			//  TMR2 is On, Scalers 1:1

	while (1 == 1) {			//  Loop forever

//  Put in Robot high level operation code here

		if (Collision != OldCollision) {
			OldCollision = Collision;	//  Save Current Collision State
			if (Collision)	//  Put in Appropriate Message
				LCDOut(Message2);
			else
				LCDOut(Message1);
			while (LCDState);	//  Wait for LCD Available
		}  //  endif

	}  //  endwhile

}  //  End of Mainline
    

remote.c - "Sony" TV Remote Control Receiver

I find infra-red TV remote controls to be very useful when working with robots - they will allow you to control the robot directly very simply and generally quite inexpensively. The \code\remote\remote.c application will display the bit pattern received from "Sony" brand TV remote controls. The \code\remote\remote.wat file was created to help me debug the application.

The source code for \code\remote\remote.c is presented below:

#include 

//  02.04.17 - Updated for New Compiler Format
//  02.02.15 - Remote: Display Sony IR Commands on an LCD
//
//  Take Input from Sony IR Remote Control and Display on LCD.
//
//  To display data, two wire interface using a 74LS174 as a shift
//   register is used and processed by the biologic code into a 
//   string displayed by the LCD.
//
//  Data Stream:
//
//            Leader/Header   High  "1"           "0"
//  -------+                 +----+     +----+            +------------
//         |                 |    |     |    |            |
//         +-----------------+    +-----+    +------------+
//
//         |   2.4 msecs     |540u|660us|540u|  1.2 msecs |
//
//                           |1200 msecs|   1.76 msecs    |
//
//                           |   300    |       440       |
//
//
//  Hardware Notes:
//  PIC16F84/PIC16F627 Running at 4 MHz with an external ceramic resonator
//  RB0 - Sony IR Input, Use Falling Interrupt
//  RB1 - CLock Signal to the LCD
//  RB2 - Data Signal to the LCD
//  

//  Global Variables
int RTC = 0;					//  Real Time Clock Counter

volatile char LCDDlay = 20;			//  Initialization Delay Value
volatile char LCDState = 1;			//  Current LCD State

static volatile bit Clock     @ (unsigned)&PORTB*8+1;
static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1;
static volatile bit Data      @ (unsigned)&PORTB*8+2;
static volatile bit DataTRIS  @ (unsigned)&TRISB*8+2;

char * MessageOut;				//  Message to be Sent Out
volatile char MessageOuti = 0;			//  Index to current Message Byte

unsigned int  DataIn;					//  Data Input
unsigned char DataInCount = 0;				//  Number of Characters to Read In
unsigned char DataReady = 0;				//  Data to be Read in 
int  SaveRTC;					//  Saved RTC/TMR0 for Interrupt Pins
int  CurrentRTC;
char Message[20] = "\f-> 0b0xxxxxxxxxxxx";	//  Header of Incoming Bits


//  Configuration Fuses
#if defined (_16F84)
#warning PIC16F84 selected
	__CONFIG(0x03FF1);		//  PIC16F84 Configuration Fuses:
						//   - XT Oscillator
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
#elif defined(_16F627)
#warning PIC16F627 with external XT oscillator selected
	__CONFIG(0x03F61);		//  PIC116F627 Configuration Fuses:
						//   - External "XT" Oscillator
						//   - RA6/RA7 Digital I/O
						//   - External Reset
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
						//   - BODEN Enabled
#else
#error Unsupported PICmicro MCU selected
#endif


//  Subroutines
LCDNybble(char Nybble, char RS) { 		//  Send Nybble to LCD

unsigned int i; 

	Data = 0; // Clear the '174 
	for (i = 0; i < 6; i++) { 		// Repeat for six bits 
		Clock = 1; Clock = 0; 		// Write the "0"s into the '174 
	}   //  endfor 

	Data = 1; 				// Output the "AND" Value 
	Clock = 1; Clock = 0; 

	Data = RS; 				// Output the RS Bit Value 
	Clock = 1; Clock = 0; 

	for (i = 0; i < 4; i++) { 		// Output the Nybble 
		if ((Nybble & 0x008) != 0) 
			Data = 1; 		// Output the High Order Bit 
		else 
			Data = 0; 
		Clock = 1; Clock = 0; 		// Strobe the Clock 
		Nybble = Nybble << 1; 		// Shift up Nybble for Next Byte 
	}  //  endfor

	Data = 1; Data = 0; 			// Toggle the "E" Clock Bit 

}  //  End LCDNybble 

LCDByte(char Byte, char RS) {	 		//  Send Byteto LCD

	LCDNybble((Byte >> 4) & 0x00F, RS);	//  Send High Nybble
	LCDNybble(Byte & 0x00F, RS);		//  Send Low Nybble

}  //  End LCDByte


LCDInit()					//  Initialize the LCD I/O Pins
{

	Clock = 0; Data = 0;			//  Low I/O Bits
	ClockTRIS = 0; DataTRIS = 0;

}  //  End LCDInit


LCDOut(char * const LCDString)	//  Output the Data String
{

	while (LCDState != 0);		//  Wait for LCD Available

	MessageOut = LCDString;		//  Load up the string
	LCDState = 100;			//  Start Sending the String

}  //  End LCDOut


//  Interrupt Handler
void interrupt tmr0_int(void)			//  TMR0 Interrupt Handler
{

char temp;

	if (T0IF) {

		T0IF = 0;			//  Reset Interrupt Flag

		RTC++;				//  Increment the Clock

//  Put additional interface code for 1 msec interrupt here

//  Check for Packet which was lost and stop waiting unless packet is active

		CurrentRTC = (RTC & 0x0FF) - ((SaveRTC >> 8) & 0x0FF);
		if (CurrentRTC < 0)		//  Calculate Time Since Save
			CurrentRTC = 0 - CurrentRTC;
		if ((DataInCount != 0) && (CurrentRTC > 9))
			DataInCount = 0;	//  Reset and Wait for the Next Character

//  LCD State Machine

		switch(LCDState) {			//  Process the State Machine information
			case 1:				//  Start LCD Initialization Process
				if (--LCDDlay == 0)
					LCDState++;
				break;			//  Wait 20 msecs for LCD to reset
			case 2:
				LCDNybble(0x003, 0);	//  Step 2 - Send Init Char
				LCDDlay = 5;
				LCDState++;
			case 3:				//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 4:
				LCDNybble(0x003, 0);	//  Step 3 - Send Init Char
				LCDState++;
				break;
			case 5:
				LCDNybble(0x003, 0);	//  Step 4 - Send Init Char
				LCDState++;
				break;
			case 6:
				LCDNybble(0x002, 0);	//  Step 5 - Set Operating
				LCDState++;		//   Interface Size (4 Bits)
				break;
			case 7:
				LCDByte(0x028, 0);	//  Step 6 - Set Operating
				LCDState++;			
				break;
			case 8:
				LCDByte(0x008, 0);	//  Step 7 - Display Off
				LCDState++;			
				break;
			case 9:
				LCDByte(0x001, 0);	//  Step 8 - Clear Display
				LCDState++;			
				LCDDlay = 5;		//  Wait 5 msecs for Clear to Complete
				break;
			case 10:			//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 11:
				LCDByte(0x006, 0);	//  Step 9 - Shift 
				LCDState++;			
				break;
			case 12:
				LCDByte(0x00E, 0);	//  Step 10 - Display On
				LCDState = 0;		//  Ready to Run
				break;

			case 100:				//  Output a Character in "MessageOut"
				switch (temp = MessageOut[MessageOuti++]) {
					case '\0':		//  At the End of the Message
						LCDState = 0;
						MessageOuti = 0;	//  Reset Message Index
						break;
					case '\f':		//  Clear Display
						LCDByte(0x001, 0);
						LCDState++;
						LCDDlay = 5;
						break;
					case 254:		//  Command Byte follows
						if ((temp = MessageOut[MessageOuti++]) == 0)
							LCDState = 0;
						else {
							if (temp < 4) {
								LCDState++;
								LCDDlay = 5;
							}  //  endif
							LCDByte(temp, 0);
						}  //  endif
						break;
					default:		//  All other characters
						LCDByte(temp, 1);
				}  //  endswitch
				break;
			case 101:				//  Message Delay
				if (--LCDDlay == 0)
					LCDState--;
				break;
		}  //  endswitch

	}  //  endif

//  Put Different Interrupt Handlers here

	if (INTF) {				//  RB0/INT Pin Interrupt
		if (DataReady)			//  Waiting to Process Previous Packet 
			;			//  Ignore
		else if (DataInCount == 0) {	//  New Message Coming In
			DataInCount = 12;	//  12 Bits to Come in
			SaveRTC = ((RTC & 0x0FF) << 8) + TMR0;	//  Save the Current Time
			DataIn = 0;
		}  else {			//  Bit to Process
			CurrentRTC = ((RTC & 0x0FF) << 8) + TMR0;	//  Get the Current Time
			if ((SaveRTC = CurrentRTC - SaveRTC) < 0) 
				SaveRTC = 0 - SaveRTC;
			if ((SaveRTC > 250) && (SaveRTC < 350)) {
				DataIn = (DataIn << 1) + 1;	//  "1" Received
				if (--DataInCount == 0)
					DataReady = 1;
			} else if ((SaveRTC > 390) && (SaveRTC < 490)) {
				DataIn = DataIn << 1;		//  "0" Received
				if (--DataInCount == 0)
					DataReady = 1;
			} else			//  Invalid - Delete
				DataInCount = 0;
			SaveRTC = CurrentRTC;	//  Save the Execution Time
		}  // endif
		INTF = 0;			//  Reset Interrupt
	}  //  endif

}  //  End Interrupt Handler



//  Mainline
void main(void)					//  Template Mainline
{
int i;

	OPTION = 0x0D1;				//  Assign Prescaler to TMR0
						//   Prescaler is /4
	TMR0 = 0;				//  Reset the Timer for Start
	T0IE = 1;				//  Enable Timer Interrupts
	GIE = 1;				//  Enable Interrupts

//  Put in Interface initialization code here

	LCDInit();				//  Initialize the LCD Port

	INTEDG = 1;				//  Interrupt on Rising Edge of RB0/IR TX'r
	INTE = 1;				//  Enable RBO/INT Pin Interrupts

	while (1 == 1) {			//  Loop forever

//  Put in Robot high level operation code here

		if (DataReady) {
			while (LCDState != 0);		//  Wait for the LCD Available
			for (i = 0; i < 12; i++) {
				if (DataIn & (1 << 11))
					Message[i + 7] = '1';
				else
					Message[i + 7] = '0';
				DataIn = DataIn << 1;
			}  //  endfor
			LCDOut(Message);	//  Pass String to Output
			DataReady = 0;		//  Reset the Data Flag				
		}  //  endif
	}  //  endwhile

}  //  End of Mainline
    

combine.c - Combining IR Object Detection and TV Remote Control Receiver Functions

With an IR receiver built into the robot for collision detection or for remote control, it is natural to want to combine these functions. This application, found in \code\combine\combine.c, filters the incoming signals and determines whether or not there is an object in front of the robot or if a command from a remote control is coming in.

#include 

//  02.04.19 - Created from "irdetect" and "remote"
//  02.02.15 - Remote: Display Sony IR Commands on an LCD
//
//  A 38 kHz IR Signal to be used as an object detection system
//
//  Take Input from Sony IR Remote Control and Display on LCD.
//
//  To display data, two wire interface using a 74LS174 as a shift
//   register is used and processed by the biologic code into a 
//   string displayed by the LCD.
//
//  Data Stream:
//
//            Leader/Header   High  "1"           "0"
//  -------+                 +----+     +----+            +------------
//         |                 |    |     |    |            |
//         +-----------------+    +-----+    +------------+
//
//         |   2.4 msecs     |540u|660us|540u|  1.2 msecs |
//
//                           |1200 msecs|   1.76 msecs    |
//
//                           |   300    |       440       |
//
//
//  Hardware Notes:
//  PIC16F627 Running at 4 MHz with an external ceramic resonator
//  RB0 - Sony IR Input, Use Falling Interrupt
//  RB1 - CLock Signal to the LCD
//  RB2 - Data Signal to the LCD
//  

//  Global Variables
int RTC = 0;					//  Real Time Clock Counter

volatile char LCDDlay = 20;			//  Initialization Delay Value
volatile char LCDState = 1;			//  Current LCD State

static volatile bit Clock     @ (unsigned)&PORTB*8+1;
static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1;
static volatile bit Data      @ (unsigned)&PORTB*8+2;
static volatile bit DataTRIS  @ (unsigned)&TRISB*8+2;

char * MessageOut;				//  Message to be Sent Out
volatile char MessageOuti = 0;			//  Index to current Message Byte

unsigned int  DataIn;					//  Data Input
unsigned char DataInCount = 0;				//  Number of Characters to Read In
unsigned char DataReady = 0;				//  Data to be Read in 
int  SaveRTC;					//  Saved RTC/TMR0 for Interrupt Pins
int  CurrentRTC;
char Message[22] = "\376\002-> 0b0xxxxxxxxxxxx!";	//  Incoming Bit Display
char Message1[12] = "\376\300         ";		//  Clear Display
char Message2[12] = "\376\300COLLISION";		//  Display "Collision"

volatile char Collision = 0;			//  Current Collision
volatile char OldCollision = 0;


//  Configuration Fuses
#if defined(_16F627)
#warning PIC16F627 with external XT oscillator selected
	__CONFIG(0x03F61);		//  PIC116F627 Configuration Fuses:
						//   - External "XT" Oscillator
						//   - RA6/RA7 Digital I/O
						//   - External Reset
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
						//   - BODEN Enabled
#else
#error Unsupported PICmicro MCU selected
#endif


//  Subroutines
LCDNybble(char Nybble, char RS) { 		//  Send Nybble to LCD

unsigned int i; 

	Data = 0; // Clear the '174 
	for (i = 0; i < 6; i++) { 		// Repeat for six bits 
		Clock = 1; Clock = 0; 		// Write the "0"s into the '174 
	}   //  endfor 

	Data = 1; 				// Output the "AND" Value 
	Clock = 1; Clock = 0; 

	Data = RS; 				// Output the RS Bit Value 
	Clock = 1; Clock = 0; 

	for (i = 0; i < 4; i++) { 		// Output the Nybble 
		if ((Nybble & 0x008) != 0) 
			Data = 1; 		// Output the High Order Bit 
		else 
			Data = 0; 
		Clock = 1; Clock = 0; 		// Strobe the Clock 
		Nybble = Nybble << 1; 		// Shift up Nybble for Next Byte 
	}  //  endfor

	Data = 1; Data = 0; 			// Toggle the "E" Clock Bit 

}  //  End LCDNybble 

LCDByte(char Byte, char RS) {	 		//  Send Byteto LCD

	LCDNybble((Byte >> 4) & 0x00F, RS);	//  Send High Nybble
	LCDNybble(Byte & 0x00F, RS);		//  Send Low Nybble

}  //  End LCDByte


LCDInit()					//  Initialize the LCD I/O Pins
{

	Clock = 0; Data = 0;			//  Low I/O Bits
	ClockTRIS = 0; DataTRIS = 0;

}  //  End LCDInit


LCDOut(char * const LCDString)	//  Output the Data String
{

	while (LCDState != 0);		//  Wait for LCD Available

	MessageOut = LCDString;		//  Load up the string
	LCDState = 100;			//  Start Sending the String

}  //  End LCDOut


//  Interrupt Handler
void interrupt tmr0_int(void)			//  TMR0 Interrupt Handler
{

char temp;

	if (T0IF) {

		T0IF = 0;			//  Reset Interrupt Flag

		RTC++;				//  Increment the Clock

//  Put additional interface code for 1 msec interrupt here

//  Check for Packet which was lost and stop waiting unless packet is active

		if (DataInCount != 0) {	//  Check/Update Time for New IR Packet Coming In

			if ((CurrentRTC = (RTC & 0x0FF) - ((SaveRTC >> 8) & 0x0FF)) < 0)	
				CurrentRTC = 0 - CurrentRTC;	//  Calculate Time Since Save
			if (CurrentRTC > 9) {	//  Delay too Long?  
				DataInCount = 0;	//  Reset and Wait for the Next Packet
				INTE = 0;	//  Disable Incoming Data Interrupts
			}  //  endif

		} else if ((!RB0) && (!DataReady)) {	//  Data Packet Coming in?  

			DataInCount = 13;	//  12 Bits to Come in/13 Edges
			SaveRTC = ((RTC & 0x0FF) << 8) + TMR0;	//  Save the Current Time
			INTF = 0;		//  Enable Pin Change Interrupt
			INTE = 1;

		} else {			//  Do Collision Detection

			TRISB3 = 0;		//  Enable PWM Output

			while (TMR0 < 64);	//  For a Limited Number of Cycles

			if (!RB0)		//  If Low, then Collision
				Collision++;	//  Must Have 3 Collisions in a Row
			else
				Collision = 0;

			TRISB3 = 1;

		}  //  endif

//  LCD State Machine

		switch(LCDState) {			//  Process the State Machine information
			case 1:				//  Start LCD Initialization Process
				if (--LCDDlay == 0)
					LCDState++;
				break;			//  Wait 20 msecs for LCD to reset
			case 2:
				LCDNybble(0x003, 0);	//  Step 2 - Send Init Char
				LCDDlay = 5;
				LCDState++;
			case 3:				//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 4:
				LCDNybble(0x003, 0);	//  Step 3 - Send Init Char
				LCDState++;
				break;
			case 5:
				LCDNybble(0x003, 0);	//  Step 4 - Send Init Char
				LCDState++;
				break;
			case 6:
				LCDNybble(0x002, 0);	//  Step 5 - Set Operating
				LCDState++;		//   Interface Size (4 Bits)
				break;
			case 7:
				LCDByte(0x028, 0);	//  Step 6 - Set Operating
				LCDState++;			
				break;
			case 8:
				LCDByte(0x008, 0);	//  Step 7 - Display Off
				LCDState++;			
				break;
			case 9:
				LCDByte(0x001, 0);	//  Step 8 - Clear Display
				LCDState++;			
				LCDDlay = 5;		//  Wait 5 msecs for Clear to Complete
				break;
			case 10:			//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 11:
				LCDByte(0x006, 0);	//  Step 9 - Shift 
				LCDState++;			
				break;
			case 12:
				LCDByte(0x00E, 0);	//  Step 10 - Display On
				LCDState = 0;		//  Ready to Run
				break;

			case 100:				//  Output a Character in "MessageOut"
				switch (temp = MessageOut[MessageOuti++]) {
					case '\0':		//  At the End of the Message
						LCDState = 0;
						MessageOuti = 0;	//  Reset Message Index
						break;
					case '\f':		//  Clear Display
						LCDByte(0x001, 0);
						LCDState++;
						LCDDlay = 5;
						break;
					case 254:		//  Command Byte follows
						if ((temp = MessageOut[MessageOuti++]) == 0)
							LCDState = 0;
						else {
							if (temp < 4) {
								LCDState++;
								LCDDlay = 5;
							}  //  endif
							LCDByte(temp, 0);
						}  //  endif
						break;
					default:		//  All other characters
						LCDByte(temp, 1);
				}  //  endswitch
				break;
			case 101:				//  Message Delay
				if (--LCDDlay == 0)
					LCDState--;
				break;
		}  //  endswitch

	}  //  endif

//  Put Different Interrupt Handlers here

	if (INTF) {				//  RB0/INT Pin Interrupt
		CurrentRTC = ((RTC & 0x0FF) << 8) + TMR0;	//  Get the Current Time
		if ((SaveRTC = CurrentRTC - SaveRTC) < 0) 
			SaveRTC = 0 - SaveRTC;
		if ((SaveRTC > 250) && (SaveRTC < 350)) {
			DataIn = (DataIn << 1) + 1;	//  "1" Received
			if (--DataInCount == 0) {
				DataReady = 1;
				INTE = 0;	//  Finished, Disable Interrupts
			}  //  endif
		} else if ((SaveRTC > 390) && (SaveRTC < 490)) {
			DataIn = DataIn << 1;		//  "0" Received
			if (--DataInCount == 0) {
				DataReady = 1;
				INTE = 0;	//  Finished, Disable Interrupts
			}  //  endif
		} else if (--DataInCount != 12) {	//  Invalid - Delete
			DataInCount = 0;
			INTE = 0;	//  Finished, Disable Interrupts
		}  //  endif
		SaveRTC = CurrentRTC;	//  Save the Execution Time
		INTF = 0;			//  Reset Interrupt
	}  //  endif

}  //  End Interrupt Handler



//  Mainline
void main(void)					//  Template Mainline
{
int i;

	OPTION = 0x0D1;				//  Assign Prescaler to TMR0
						//   Prescaler is /4
	TMR0 = 0;				//  Reset the Timer for Start
	T0IE = 1;				//  Enable Timer Interrupts
	GIE = 1;				//  Enable Interrupts

//  Put in Interface initialization code here

	LCDInit();				//  Initialize the LCD Port

	INTEDG = 1;				//  Interrupt on Rising Edge of RB0/INT IR TX'r

	CCPR1L = 13;				//  50% Duty Cycle
	CCP1CON = 0b000001111;			//  Turn on PWM Mode

	PR2 = 26;				//  26 usec Cycle for 38 KHz
	TMR2 = 0;				//  Reset TMR2
	T2CON = 0b000000100;			//  TMR2 is On, Scalers 1:1

	while (1 == 1) {			//  Loop forever

//  Put in Robot high level operation code here

		if (DataReady) {
			while (LCDState != 0);		//  Wait for the LCD Available
			for (i = 0; i < 12; i++) {
				if (DataIn & (1 << 11))
					Message[i + 8] = '1';
				else
					Message[i + 8] = '0';
				DataIn = DataIn << 1;
			}  //  endfor
			LCDOut(Message);	//  Pass String to Output
			DataReady = 0;		//  Reset the Data Flag				
		}  //  endif

		if ((Collision == 0) && (OldCollision == 3)) {
			OldCollision = Collision;	//  Save Current Collision State
			LCDOut(Message1);
		}  //  endif
		if ((Collision == 3) && (OldCollision == 0)) {
			OldCollision = Collision;	//  Save Current Collision State
			LCDOut(Message2);
		}  //  endif

	}  //  endwhile

}  //  End of Mainline
    

ultra.c - Ultrasonic Object Detection and Ranging

IR object detection can determine the distance to an object to some degree, but for reasonable precision (ie down to an inch or less), another method, like ultrasonic ranging is required. There are a lot of different ultrasonic ranging modules available, with the most popular being the Polaroid 6500.

\code\ultra\ultra.c interfaces with a Polaroid 6500 to determine the distance from the 6500's transducer to an object. To debug the application, I came up with the MPLAB IDE stimulus file \code\ultra\ultra.sti. The source code for the example application is:

#include 

//  02.04.19 - ULTRA: Get the distance from the Polaroid 6500
//   Transducer and Display it on a LCD
//
//  This is a two wire interface using a 74LS174 as a shift
//   register and the code called from the mainline (and timed 
//   from TMR0 interrupt)
//
//
//  Hardware Notes:
//  PIC16F827 Running at 4 MHz with External Oscillator
//  RB0 - Pulled Up "ECHO" from Polaroid 6500
//  RB1 - CLock Signal to the LCD
//  RB2 - Data Signal to the LCD
//  RB3 - "INIT" Line to start the distance measurement
//  

//  Global Variables
unsigned int RTC = 0;				//  Real Time Clock Counter

volatile char LCDDlay = 20;			//  Initialization Delay Value
volatile char LCDState = 1;			//  Current LCD State

static volatile bit Clock     @ (unsigned)&PORTB*8+1;
static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1;
static volatile bit Data      @ (unsigned)&PORTB*8+2;
static volatile bit DataTRIS  @ (unsigned)&TRISB*8+2;

char * MessageOut;			//  Message to be Sent Out
volatile char MessageOuti = 0;	//  Index to current Message Byte
char Message[9] = "\fxx\' xx\"";	//  Distance Measurement
char Message2[9] = "\fInvalid";	//  "Invalid" Distance Measurement Message

unsigned int  CheckDlay = 50;		//  Wait 500 msecs between Range Findings
unsigned int  PulseStartRTC;		//  RTC at Pulse Send
unsigned int  PulseEndRTC;		//  RTC at Pulse End
unsigned char PulseEndTMR0;		//  TMR0 Value for Pulse End
unsigned char PulseState = 0;		//  0 - Waiting to Check Distance
						//  1 - Distance Measurement Pending
						//  2 - Have Distance
						//  3 - Invalid Distance
unsigned long PulseTime;		//  Number of msecs for the Pulse Time
unsigned long PulseInches;		//  Number of Inches measured
unsigned long PulseFeet;		//  Number of Feet measured

//  Configuration Fuses
#if defined (_16F84)
#warning PIC16F84 selected
	__CONFIG(0x03FF1);		//  PIC16F84 Configuration Fuses:
						//   - XT Oscillator
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
#elif defined(_16F627)
#warning PIC16F627 with external XT oscillator selected
	__CONFIG(0x03F61);		//  PIC116F627 Configuration Fuses:
						//   - External "XT" Oscillator
						//   - RA6/RA7 Digital I/O
						//   - External Reset
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
						//   - BODEN Enabled
#else
#error Unsupported PICmicro MCU selected
#endif


//  Subroutines
LCDNybble(char Nybble, char RS) { 		//  Send Nybble to LCD

unsigned int i; 

	Data = 0; // Clear the '174 
	for (i = 0; i < 6; i++) { 		// Repeat for six bits 
		Clock = 1; Clock = 0; 		// Write the "0"s into the '174 
	}   //  endfor 

	Data = 1; 				// Output the "AND" Value 
	Clock = 1; Clock = 0; 

	Data = RS; 				// Output the RS Bit Value 
	Clock = 1; Clock = 0; 

	for (i = 0; i < 4; i++) { 		// Output the Nybble 
		if ((Nybble & 0x008) != 0) 
			Data = 1; 		// Output the High Order Bit 
		else 
			Data = 0; 
		Clock = 1; Clock = 0; 		// Strobe the Clock 
		Nybble = Nybble << 1; 		// Shift up Nybble for Next Byte 
	}  //  endfor

	Data = 1; Data = 0; 			// Toggle the "E" Clock Bit 

}  //  End LCDNybble 

LCDByte(char Byte, char RS) {	 		//  Send Byteto LCD

	LCDNybble((Byte >> 4) & 0x00F, RS);	//  Send High Nybble
	LCDNybble(Byte & 0x00F, RS);		//  Send Low Nybble

}  //  End LCDByte


LCDInit()					//  Initialize the LCD I/O Pins
{

	Clock = 0; Data = 0;			//  Low I/O Bits
	ClockTRIS = 0; DataTRIS = 0;

}  //  End LCDInit


LCDOut(char * const LCDString)	//  Output the Data String
{

	while (LCDState);		//  Wait for LCD Available

	MessageOut = LCDString;		//  Load up the string
	LCDState = 100;			//  Start Sending the String

}  //  End LCDOut


//  Interrupt Handler
void interrupt tmr0_int(void)			//  TMR0 Interrupt Handler
{

char temp;

	if (T0IF) {

		T0IF = 0;			//  Reset Interrupt Flag

		RTC++;				//  Increment the Clock

//  Put additional interface code for 1 msec interrupt here

//  Ultra sonic operations

		if (--CheckDlay == 0) {	//  Start the Pulse Output
			PulseStartRTC = RTC;
			PulseState = 1;	//  Doing Distance Measurement
			RB3 = 1;		//  Start Pulse
			INTF = 0;		//  Make sure no Pending Interrupts
			INTE = 1;		//  Enable RB0/INTF
			CheckDlay = 500;	//  Wait another 1/2 Second
			if (RTC > 64900)	//  Make sure no issue with Crossing RTC Roll Over
				CheckDlay += 200;
		}  //  endif

		if ((PulseState == 1) && ((PulseStartRTC + 60) < RTC)) {
			RB3 = 0;		//  Invalid delay, Turn off
			INTF = 0;		//   6500 and requests
			INTE = 0;
			PulseState = 3;	//  Invalid
		}  //  endif

//  LCD State Machine

		switch(LCDState) {			//  Process the State Machine information
			case 1:				//  Start LCD Initialization Process
				if (--LCDDlay == 0)
					LCDState++;
				break;			//  Wait 20 msecs for LCD to reset
			case 2:				//
				LCDNybble(0x003, 0);	//  Step 2 - Send Init Char
				LCDDlay = 5;
				LCDState++;
			case 3:				//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 4:
				LCDNybble(0x003, 0);	//  Step 3 - Send Init Char
				LCDState++;
				break;
			case 5:
				LCDNybble(0x003, 0);	//  Step 4 - Send Init Char
				LCDState++;
				break;
			case 6:
				LCDNybble(0x002, 0);	//  Step 5 - Set Operating
				LCDState++;		//   Interface Size (4 Bits)
				break;
			case 7:
				LCDByte(0x028, 0);	//  Step 6 - Set Operating
				LCDState++;			
				break;
			case 8:
				LCDByte(0x008, 0);	//  Step 7 - Display Off
				LCDState++;			
				break;
			case 9:
				LCDByte(0x001, 0);	//  Step 8 - Clear Display
				LCDState++;			
				LCDDlay = 5;		//  Wait 5 msecs for Clear to Complete
				break;
			case 10:			//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 11:
				LCDByte(0x006, 0);	//  Step 9 - Shift 
				LCDState++;			
				break;
			case 12:
				LCDByte(0x00E, 0);	//  Step 10 - Display On
				LCDState = 0;		//  Ready to Run
				break;

			case 100:				//  Output a Character in "MessageOut"
				switch (temp = MessageOut[MessageOuti++]) {
					case '\0':		//  At the End of the Message
						LCDState = 0;
						MessageOuti = 0;	//  Reset Message Index
						break;
					case '\f':		//  Clear Display
						LCDByte(0x001, 0);
						LCDState++;
						LCDDlay = 5;
						break;
					case 254:		//  Command Byte follows
						if ((temp = MessageOut[MessageOuti++]) == 0)
							LCDState = 0;
						else {
							if (temp < 4) {
								LCDState++;
								LCDDlay = 5;
							}  //  endif
							LCDByte(temp, 0);
						}  //  endif
						break;
					default:		//  All other characters
						LCDByte(temp, 1);
				}  //  endswitch
				break;
			case 101:				//  Message Delay
				if (--LCDDlay == 0)
					LCDState--;
				break;
		}  //  endswitch

	}  //  endif

//  Put Different Interrupt Handlers here

	if (INTF) {
		PulseEndTMR0 = TMR0;		//  Recored the Pulse End
		PulseEndRTC = RTC;
		RB3 = 0;				//  Reset Measurement Request
		PulseState = 2;			//  Have the Pulse
		INTF = 0;				//  Turn Off Interrupts
		INTE = 0;
	}  // endif

}  //  End Interrupt Handler


//  Mainline
void main(void)					//  Template Mainline
{

unsigned int temp;				//  Temporary Storage Value

	OPTION = 0x0D1;				//  Assign Prescaler to TMR0
						//   Prescaler is /4
	TMR0 = 0;				//  Reset the Timer for Start
	T0IE = 1;				//  Enable Timer Interrupts
	GIE = 1;				//  Enable Interrupts

	LCDInit();				//  Initialize the LCD Port

//  Put in Interface initialization code here

	RB3 = 0;				//  Enable RB3 for Output
	TRISB3 = 0;				//  Keep Low Initially

	while (1 == 1) {			//  Loop forever

//  Put in Robot high level operation code here

		switch(PulseState) {	//  Process according to the Pulse State
			case 2:		//  Have Distance, Print Measurement
				PulseTime = (((long) PulseEndRTC * 256) + (long) PulseEndTMR0) * 40;
				PulseTime -= ((long) PulseStartRTC * (256 * 40));
				PulseInches = PulseTime / 1533;
				PulseFeet = PulseInches / 12;
				PulseInches = PulseInches % 12;
				if ((temp = PulseFeet / 10) == 0)
					Message[1] = ' ';	//  Distance Not 10s of feet
				else
					Message[1] = temp + '0';
				Message[2] = (PulseFeet % 10) + '0';
				if ((temp = PulseInches / 10) == 0)
					Message[5] = ' ';
				else
					Message[5] = temp + '0';
				Message[6] = (PulseInches % 10) + '0';
				LCDOut(Message);
				PulseState = 0;	//  Wait for the Next Pulse
				break;
			case 3:		//  Invalid for some reason
				LCDOut(Message2);
				PulseState = 0;	//  Wait for the Next Pulse
				break;
		}  //  endswitch

	}  //  endwhile

}  //  End of Mainline
    

light1.c - Using the PIC16F627 Voltage Comparator to Detect Differing Light Levels

I will demonstrate two ways in which light can be detected in a robot. The first method, found in \code\light\light1.c wires two CDS cells as a voltage divider and responds to which ever device has the lowest resistance. The lower resistance is assumed to indicate that CDS cell is being exposed to more light. The source code for the application is listed below:

#include 

//  02.04.24 - LIGHT: Look at two CDS Cells in Series and indicate
//   brighter one
//
//  This is a two wire interface using a 74LS174 as a shift
//   register and the code called from the mainline (and timed 
//   from TMR0 interrupt)
//
//
//  Hardware Notes:
//  PIC16F827 Running at 4 MHz with External Oscillator
//  RA0 - Comparator2, Connected to a 2 CDS Cell Voltage Divider 
//   with "Left" on Top
//  RA1 - Comparator1, Tied to Vss
//  RA2 - 1/2 Vdd from Vref module, output to check Vref operation
//  RB1 - CLock Signal to the LCD
//  RB2 - Data Signal to the LCD
//  

//  Global Variables
int RTC = 0;					//  Real Time Clock Counter

volatile char LCDDlay = 20;			//  Initialization Delay Value
volatile char LCDState = 1;			//  Current LCD State

static volatile bit Clock     @ (unsigned)&PORTB*8+1;
static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1;
static volatile bit Data      @ (unsigned)&PORTB*8+2;
static volatile bit DataTRIS  @ (unsigned)&TRISB*8+2;

char * MessageOut;				//  Message to be Sent Out
volatile char MessageOuti = 0;			//  Index to current Message Byte
char MessageL[7] = "\376\200*\376\220 ";	//  Put "*" on Left
//                   1   2  34   5   67
char MessageR[7] = "\376\200 \376\220*";	//  Put "*" on Left


//  Configuration Fuses
#if defined(_16F627)
//  #warning PIC16F627 with external XT oscillator selected
	__CONFIG(0x03F61);		//  PIC116F627 Configuration Fuses:
						//   - External "XT" Oscillator
						//   - RA6/RA7 Digital I/O
						//   - External Reset
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
						//   - BODEN Enabled
#else
#error Unsupported PICmicro MCU selected
#endif


//  Subroutines
LCDNybble(char Nybble, char RS) { 		//  Send Nybble to LCD

unsigned int i; 

	Data = 0; // Clear the '174 
	for (i = 0; i < 6; i++) { 		// Repeat for six bits 
		Clock = 1; Clock = 0; 		// Write the "0"s into the '174 
	}   //  endfor 

	Data = 1; 				// Output the "AND" Value 
	Clock = 1; Clock = 0; 

	Data = RS; 				// Output the RS Bit Value 
	Clock = 1; Clock = 0; 

	for (i = 0; i < 4; i++) { 		// Output the Nybble 
		if ((Nybble & 0x008) != 0) 
			Data = 1; 		// Output the High Order Bit 
		else 
			Data = 0; 
		Clock = 1; Clock = 0; 		// Strobe the Clock 
		Nybble = Nybble << 1; 		// Shift up Nybble for Next Byte 
	}  //  endfor

	Data = 1; Data = 0; 			// Toggle the "E" Clock Bit 

}  //  End LCDNybble 

LCDByte(char Byte, char RS) {	 		//  Send Byteto LCD

	LCDNybble((Byte >> 4) & 0x00F, RS);	//  Send High Nybble
	LCDNybble(Byte & 0x00F, RS);		//  Send Low Nybble

}  //  End LCDByte


LCDInit()					//  Initialize the LCD I/O Pins
{

	Clock = 0; Data = 0;			//  Low I/O Bits
	ClockTRIS = 0; DataTRIS = 0;

}  //  End LCDInit


LCDOut(char * const LCDString)	//  Output the Data String
{

	while (LCDState);		//  Wait for LCD Available

	MessageOut = LCDString;		//  Load up the string
	LCDState = 100;			//  Start Sending the String

}  //  End LCDOut


int ADCPoll()
{

	return C2OUT;

}  //  End ADCPoll


//  Interrupt Handler
void interrupt tmr0_int(void)			//  TMR0 Interrupt Handler
{

char temp;

	if (T0IF) {

		T0IF = 0;			//  Reset Interrupt Flag

		RTC++;				//  Increment the Clock

//  Put additional interface code for 1 msec interrupt here

//  LCD State Machine

		switch(LCDState) {			//  Process the State Machine information
			case 1:				//  Start LCD Initialization Process
				if (--LCDDlay == 0)
					LCDState++;
				break;			//  Wait 20 msecs for LCD to reset
			case 2:				//
				LCDNybble(0x003, 0);	//  Step 2 - Send Init Char
				LCDDlay = 5;
				LCDState++;
			case 3:				//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 4:
				LCDNybble(0x003, 0);	//  Step 3 - Send Init Char
				LCDState++;
				break;
			case 5:
				LCDNybble(0x003, 0);	//  Step 4 - Send Init Char
				LCDState++;
				break;
			case 6:
				LCDNybble(0x002, 0);	//  Step 5 - Set Operating
				LCDState++;		//   Interface Size (4 Bits)
				break;
			case 7:
				LCDByte(0x028, 0);	//  Step 6 - Set Operating
				LCDState++;			
				break;
			case 8:
				LCDByte(0x008, 0);	//  Step 7 - Display Off
				LCDState++;			
				break;
			case 9:
				LCDByte(0x001, 0);	//  Step 8 - Clear Display
				LCDState++;			
				LCDDlay = 5;		//  Wait 5 msecs for Clear to Complete
				break;
			case 10:			//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 11:
				LCDByte(0x006, 0);	//  Step 9 - Shift 
				LCDState++;			
				break;
			case 12:
//				LCDByte(0x00E, 0);	//  Step 10 - Display On
				LCDByte(0x00C, 0);	//  Step 10 - Display On/No Cursor
				LCDState = 0;		//  Ready to Run
				break;

			case 100:				//  Output a Character in "MessageOut"
				switch (temp = MessageOut[MessageOuti++]) {
					case '\0':		//  At the End of the Message
						LCDState = 0;
						MessageOuti = 0;	//  Reset Message Index
						break;
					case '\f':		//  Clear Display
						LCDByte(0x001, 0);
						LCDState++;
						LCDDlay = 5;
						break;
					case 254:		//  Command Byte follows
						if ((temp = MessageOut[MessageOuti++]) == 0)
							LCDState = 0;
						else {
							if (temp < 4) {
								LCDState++;
								LCDDlay = 5;
							}  //  endif
							LCDByte(temp, 0);
						}  //  endif
						break;
					default:		//  All other characters
						LCDByte(temp, 1);
				}  //  endswitch
				break;
			case 101:				//  Message Delay
				if (--LCDDlay == 0)
					LCDState--;
				break;
		}  //  endswitch

	}  //  endif

//  Put Different Interrupt Handlers here

}  //  End Interrupt Handler


//  Mainline
void main(void)					//  Template Mainline
{

	OPTION = 0x0D1;				//  Assign Prescaler to TMR0
						//   Prescaler is /4
	TMR0 = 0;				//  Reset the Timer for Start
	T0IE = 1;				//  Enable Timer Interrupts
	GIE = 1;				//  Enable Interrupts

//  Put in Interface initialization code here

	LCDInit();				//  Initialize the LCD Port

	CMCON = 0x002;				//  Enable the Comparator Module
						//   C1/C2 Normal, not inverted
						//   CIS = 0
						//   Mode 2, compare against Vdd
	TRISA = 0x007;				//  Just bits RA0 and RA1 are inputs
	VRCON = 0x0EC;				//  Enable Vref module
						//   Vref output on RA2
						//   High Vref Range
						//   Ladder value of 12

	while (1 == 1) {			//  Loop forever

//  Put in Robot high level operation code here

		if (!ADCPoll())			//  Bit 7 of CMCOM
			LCDOut(MessageL);	//  Light is left
		else
			LCDOut(MessageR);	//  Light is right

	}  //  endwhile

}  //  End of Mainline
    

light2.c - Measuring the resistance of a CDS Cell

\code\light\light2.c differs from the previous application by the way it compares the resistance of two CDS cells. \code\light\light2.c calculates a numeric value for each of the CDS cell's resistance. In a robot, this numeric value would be compared. The source code for the application is:

#include 

//  02.04.24 - LIGHT2: Implement a dual RC Network ADC on the 
//   PIC16F84/PIC16F627.  10K CDS Cell with a 0.01 uF Capacitor
//
//  This is a two wire interface using a 74LS174 as a shift
//   register and the code called from the mainline (and timed 
//   from TMR0 interrupt)
//
//
//  Hardware Notes:
//  PIC16F827 Running at 4 MHz with External Oscillator
//  RB1 - CLock Signal to the LCD
//  RB2 - Data Signal to the LCD
//  RB4 - Left ADC 
//  RB5 - Right ADC
//  

//  Global Variables
int RTC = 0;					//  Real Time Clock Counter

volatile char LCDDlay = 20;			//  Initialization Delay Value
volatile char LCDState = 1;			//  Current LCD State

static volatile bit Clock     @ (unsigned)&PORTB*8+1;
static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1;
static volatile bit Data      @ (unsigned)&PORTB*8+2;
static volatile bit DataTRIS  @ (unsigned)&TRISB*8+2;

char * MessageOut;				//  Message to be Sent Out
volatile char MessageOuti = 0;		//  Index to current Message Byte
char MessageL[19] = "\376\200               *";	//  Move Cursor to Start with bar Graph
//                   1   2   34567890123456789
char MessageR[19] = "\376\300               *";	//  Move Cursor to 2nd Line with bar Graph

volatile char ADCState = 0;		//  Current ADC State Machine Operating State
volatile char ADCDlay = 1;		//  Delay 10 msecs between each ADC Operation
volatile char LeftADC = 0;		//  Last Left ADC Value
volatile char RightADC = 0;		//  Last Right ADC Value


//  Configuration Fuses
#if defined(_16F627)
#warning PIC16F627 with external XT oscillator selected
	__CONFIG(0x03F61);		//  PIC116F627 Configuration Fuses:
						//   - External "XT" Oscillator
						//   - RA6/RA7 Digital I/O
						//   - External Reset
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
						//   - BODEN Enabled
#else
#error Unsupported PICmicro MCU selected
#endif


//  Subroutines
LCDNybble(char Nybble, char RS) { 		//  Send Nybble to LCD

unsigned int i; 

	Data = 0; // Clear the '174 
	for (i = 0; i < 6; i++) { 		// Repeat for six bits 
		Clock = 1; Clock = 0; 		// Write the "0"s into the '174 
	}   //  endfor 

	Data = 1; 				// Output the "AND" Value 
	Clock = 1; Clock = 0; 

	Data = RS; 				// Output the RS Bit Value 
	Clock = 1; Clock = 0; 

	for (i = 0; i < 4; i++) { 		// Output the Nybble 
		if ((Nybble & 0x008) != 0) 
			Data = 1; 		// Output the High Order Bit 
		else 
			Data = 0; 
		Clock = 1; Clock = 0; 		// Strobe the Clock 
		Nybble = Nybble << 1; 		// Shift up Nybble for Next Byte 
	}  //  endfor

	Data = 1; Data = 0; 			// Toggle the "E" Clock Bit 

}  //  End LCDNybble 

LCDByte(char Byte, char RS) {	 		//  Send Byteto LCD

	LCDNybble((Byte >> 4) & 0x00F, RS);	//  Send High Nybble
	LCDNybble(Byte & 0x00F, RS);		//  Send Low Nybble

}  //  End LCDByte


LCDInit()					//  Initialize the LCD I/O Pins
{

	Clock = 0; Data = 0;			//  Low I/O Bits
	ClockTRIS = 0; DataTRIS = 0;

}  //  End LCDInit


LCDOut(char * const LCDString)	//  Output the Data String
{

	while (LCDState);		//  Wait for LCD Available

	MessageOut = LCDString;		//  Load up the string
	LCDState = 100;			//  Start Sending the String

}  //  End LCDOut


//  Interrupt Handler
void interrupt tmr0_int(void)			//  TMR0 Interrupt Handler
{

char temp;

	if (T0IF) {

		T0IF = 0;			//  Reset Interrupt Flag

		RTC++;				//  Increment the Clock

//  Put additional interface code for 1 msec interrupt here

//  LCD State Machine

		switch(LCDState) {			//  Process the State Machine information
			case 1:				//  Start LCD Initialization Process
				if (--LCDDlay == 0)
					LCDState++;
				break;			//  Wait 20 msecs for LCD to reset
			case 2:				//
				LCDNybble(0x003, 0);	//  Step 2 - Send Init Char
				LCDDlay = 5;
				LCDState++;
			case 3:				//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 4:
				LCDNybble(0x003, 0);	//  Step 3 - Send Init Char
				LCDState++;
				break;
			case 5:
				LCDNybble(0x003, 0);	//  Step 4 - Send Init Char
				LCDState++;
				break;
			case 6:
				LCDNybble(0x002, 0);	//  Step 5 - Set Operating
				LCDState++;		//   Interface Size (4 Bits)
				break;
			case 7:
				LCDByte(0x028, 0);	//  Step 6 - Set Operating
				LCDState++;			
				break;
			case 8:
				LCDByte(0x008, 0);	//  Step 7 - Display Off
				LCDState++;			
				break;
			case 9:
				LCDByte(0x001, 0);	//  Step 8 - Clear Display
				LCDState++;			
				LCDDlay = 5;		//  Wait 5 msecs for Clear to Complete
				break;
			case 10:			//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 11:
				LCDByte(0x006, 0);	//  Step 9 - Shift 
				LCDState++;			
				break;
			case 12:
//				LCDByte(0x00E, 0);	//  Step 10 - Display On
				LCDByte(0x00C, 0);	//  Step 10 - Cursor Off
				LCDState = 0;		//  Ready to Run
				break;

			case 100:				//  Output a Character in "MessageOut"
				switch (temp = MessageOut[MessageOuti++]) {
					case '\0':		//  At the End of the Message
						LCDState = 0;
						MessageOuti = 0;	//  Reset Message Index
						break;
					case '\f':		//  Clear Display
						LCDByte(0x001, 0);
						LCDState++;
						LCDDlay = 5;
						break;
					case 254:		//  Command Byte follows
						if ((temp = MessageOut[MessageOuti++]) == 0)
							LCDState = 0;
						else {
							if (temp < 4) {
								LCDState++;
								LCDDlay = 5;
							}  //  endif
							LCDByte(temp, 0);
						}  //  endif
						break;
					default:		//  All other characters
						LCDByte(temp, 1);
				}  //  endswitch
				break;
			case 101:				//  Message Delay
				if (--LCDDlay == 0)
					LCDState--;
				break;
		}  //  endswitch

//  ADC State Machine Read Here

		if (!LCDState) {			//  Only Execute if LCD NOT Active
			switch (ADCState) {
				case 0:		//  ADC Start Delay
					if (--ADCDlay == 0)
						ADCState++;
					break;
				case 1:		//  Start Charging Left and Right (for 1 msec min.)
					TRISB4 = 0;
					TRISB5 = 0;
					RB4 = 1;
					RB5 = 1;
					ADCState++;	//  Jump to next state in 1 msec
					break;
				case 2:
					TRISB4 = 1;	//  Turn off Left Pin Driver
					temp = PORTB;  //  Clear pending Interrupts on Port Change
					RBIF = 0;
					RBIE = 1;
					ADCState++;
					break;
				case 3:		//  Roll Over, No Interrupt
					LeftADC = 0x0FF;
					temp = PORTB;  //  Clear pending Interrupts on Port Change
					RBIF = 0;
					RBIE = 0;
					ADCState++;
					break;
				case 4:		//  Start Charging Right (for 1 msec)
					TRISB5 = 1;	//  Change TRISB to Input
					temp = PORTB;  //  Clear pending Interrupts on Port Change
					RBIF = 0;
					RBIE = 1;
					ADCState++;
					break;
				case 5:		//  Roll Over, No Interrupt
					RightADC = 0x0FF;
					temp = PORTB;  //  Clear pending Interrupts on Port Change
					RBIF = 0;
					RBIE = 0;
					ADCDlay = 10;
					ADCState = 0;
					break;
			}  //  endswitch
		}  //  endif

	}  //  endif

//  Put Different Interrupt Handlers here

	if (RBIF) {					//  Interrupt on Pin Change
		switch(ADCState) {
			case 3:			//  Left ADC Active
				LeftADC = TMR0;	//  Get the ADC Count
				ADCState++;
				break;
			case 5:			//  Right ADC Active
				RightADC = TMR0;	//  Get the ADC Count
				ADCDlay = 10;
				ADCState = 0;
				break;
		}  //  endswitch
		temp = PORTB;  			//  Clear pending Interrupts on Port Change
		RBIF = 0;
		RBIE = 0;
	}  //  endif

}  //  End Interrupt Handler


//  Mainline
void main(void)					//  Template Mainline
{

unsigned int i, j;
unsigned int tempLeft, tempRight;

	OPTION = 0x0D1;				//  Assign Prescaler to TMR0
						//   Prescaler is /4
	TMR0 = 0;				//  Reset the Timer for Start
	T0IE = 1;				//  Enable Timer Interrupts
	GIE = 1;				//  Enable Interrupts

//  Put in Interface initialization code here

	LCDInit();				//  Initialize the LCD Port

	while (1 == 1) {			//  Loop forever

//  Put in Robot high level operation code here

		while (ADCState != 2);	//  Wait for ADC Operation to Start
		while (ADCState);	//  Wait for ADC Operation to Complete

		tempLeft = LeftADC;  tempRight = RightADC;
						//  Save ADC Values

		j = (tempLeft / 8) + 1;		//  Calculate the number of Splats for Left
		for (i = 2; i < 18; i++ )
			if ((i - 2) <= j)	//  Print a Splat
				MessageL[i] = '*';
			else			//  Print a Blank
				MessageL[i] = ' ';

		j = (tempRight / 8) + 1;	//  Calculate the number of Splats for Right
		for (i = 2; i < 18; i++ )
			if ((i - 2) <= j)	//  Print a Splat
				MessageR[i] = '*';
			else			//  Print a Blank
				MessageR[i] = ' ';

		LCDOut(MessageL);		//  Left Graph
		LCDOut(MessageR);		//  Right Graph

	}  //  endwhile

}  //  End of Mainline
    

sound.c - Listening for commands

The last method that a robot can use for sensing its outside environment is to listen for sounds. \code\sound\sound.c will indicate when a microphone receives a loud sound which has been filtered to its basic components. For debugging, I created \code\sound\sound.wat. The source code for the application is:

#include 

//  02.01.27 - Sound: Display Message when sound received 
//   (and finished) on LCD
//
//  The sound input is filtered and amplified using a 324 
//   op-amp.  To remove small transients, the application 
//   Checks the TMR1 count once every 20 msecs and compares
//   the current value to see if it has increased by 5 over
//   the previous 20 msec poll.  
//
//  This is a two wire interface using a 74LS174 as a shift
//   register and the code called from the mainline (and timed 
//   from TMR0 interrupt)
//
//
//  Hardware Notes:
//  PIC16F84 Running at 4 MHz
//  RB1 - CLock Signal to the LCD
//  RB2 - Data Signal to the LCD
//  

//  Global Variables
int RTC = 0;					//  Real Time Clock Counter

volatile char LCDDlay = 20;			//  Initialization Delay Value
volatile char LCDState = 1;			//  Current LCD State

static volatile bit Clock     @ (unsigned)&PORTB*8+1;
static volatile bit ClockTRIS @ (unsigned)&TRISB*8+1;
static volatile bit Data      @ (unsigned)&PORTB*8+2;
static volatile bit DataTRIS  @ (unsigned)&TRISB*8+2;

char * MessageOut;				//  Message to be Sent Out
volatile char MessageOuti = 0;			//  Index to current Message Byte

char Message1[18] = "\376\200sound - 0x0xxxx";	//  Message when sound
//                   1   2   3456789012345678
char Message2[18] = "\376\200               ";	//  Message when nothing

char soundCounter = 20;			//  Wait 20 msecs for Sound Check
int OldTMR1 = 0;				//  Timer Values
int CurrentTMR1;
volatile char soundState = 0;		//  Sound State Variable
						//   0 - No Sound
						//   1 - Sound Received, Check Next
						//   2 - Sound Received, Ended


//  Configuration Fuses
#if defined(_16F627)
#warning PIC16F627 with external XT oscillator selected
	__CONFIG(0x03F61);		//  PIC116F627 Configuration Fuses:
						//   - External "XT" Oscillator
						//   - RA6/RA7 Digital I/O
						//   - External Reset
						//   - 70 msecs Power Up Timer On
						//   - Watchdog Timer Off
						//   - Code Protection Off
						//   - BODEN Enabled
#else
#error Unsupported PICmicro MCU selected
#endif


//  Subroutines
char GetHex(char Value)
{

char returnValue;

	if (Value > 9)
		returnValue = Value + 'A' - 10;
	else
		returnValue = Value + '0';

	return returnValue;

}  //  End GetHex


Dlay(int msecs)				//  Delay Specified Number of msecs
{

volatile int variableDlay;		//  1 Second Delay Variable

	variableDlay = RTC + msecs + 1;	//  Get the End Time
	while (variableDlay != RTC);

}  //  End Dlay


LCDNybble(char Nybble, char RS) { 		//  Send Nybble to LCD

unsigned int i; 

	Data = 0; // Clear the '174 
	for (i = 0; i < 6; i++) { 		// Repeat for six bits 
		Clock = 1; Clock = 0; 		// Write the "0"s into the '174 
	}   //  endfor 

	Data = 1; 				// Output the "AND" Value 
	Clock = 1; Clock = 0; 

	Data = RS; 				// Output the RS Bit Value 
	Clock = 1; Clock = 0; 

	for (i = 0; i < 4; i++) { 		// Output the Nybble 
		if ((Nybble & 0x008) != 0) 
			Data = 1; 		// Output the High Order Bit 
		else 
			Data = 0; 
		Clock = 1; Clock = 0; 		// Strobe the Clock 
		Nybble = Nybble << 1; 		// Shift up Nybble for Next Byte 
	}  //  endfor

	Data = 1; Data = 0; 			// Toggle the "E" Clock Bit 

}  //  End LCDNybble 

LCDByte(char Byte, char RS) {	 		//  Send Byteto LCD

	LCDNybble((Byte >> 4) & 0x00F, RS);	//  Send High Nybble
	LCDNybble(Byte & 0x00F, RS);		//  Send Low Nybble

}  //  End LCDByte


LCDInit()					//  Initialize the LCD I/O Pins
{

	Clock = 0; Data = 0;			//  Low I/O Bits
	ClockTRIS = 0; DataTRIS = 0;

}  //  End LCDInit


LCDOut(char * const LCDString)	//  Output the Data String
{

	while (LCDState);		//  Wait for LCD Available

	MessageOut = LCDString;		//  Load up the string
	LCDState = 100;			//  Start Sending the String

}  //  End LCDOut


//  Interrupt Handler
void interrupt tmr0_int(void)			//  TMR0 Interrupt Handler
{

char temp;
int  TMR1Value;

	if (T0IF) {

		T0IF = 0;			//  Reset Interrupt Flag

		RTC++;				//  Increment the Clock

//  Put additional interface code for 1 msec interrupt here

//  Sound Check Software

		if (soundState == 0) 	//  Check the Current Sound Value
			if (--soundCounter == 0) {	//  Time to Check?  
				soundCounter = 20;	//  Reset
				TMR1Value = (TMR1H * 0x0100) + TMR1L;
				if (TMR1Value >= (OldTMR1 + 2))
					soundState = 1;	//  Indicate Sound Received
				OldTMR1 = TMR1Value;	//  Save Current Counter
			} else;
		else if (soundState == 1) //  Check to see if Sound still active
			if (--soundCounter == 0) {	//  Time to Check?
				soundCounter = 20;	//  Reset
				TMR1Value = (TMR1H * 0x0100) + TMR1L;
				if (TMR1Value < (OldTMR1 + 2))
					soundState = 2;	//  Indicate Sound Finished
				OldTMR1 = TMR1Value;	//  Save Current Counter
			} else;

//  LCD State Machine

		switch(LCDState) {			//  Process the State Machine information
			case 1:				//  Start LCD Initialization Process
				if (--LCDDlay == 0)
					LCDState++;
				break;			//  Wait 20 msecs for LCD to reset
			case 2:				//
				LCDNybble(0x003, 0);	//  Step 2 - Send Init Char
				LCDDlay = 5;
				LCDState++;
			case 3:				//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 4:
				LCDNybble(0x003, 0);	//  Step 3 - Send Init Char
				LCDState++;
				break;
			case 5:
				LCDNybble(0x003, 0);	//  Step 4 - Send Init Char
				LCDState++;
				break;
			case 6:
				LCDNybble(0x002, 0);	//  Step 5 - Set Operating
				LCDState++;		//   Interface Size (4 Bits)
				break;
			case 7:
				LCDByte(0x028, 0);	//  Step 6 - Set Operating
				LCDState++;			
				break;
			case 8:
				LCDByte(0x008, 0);	//  Step 7 - Display Off
				LCDState++;			
				break;
			case 9:
				LCDByte(0x001, 0);	//  Step 8 - Clear Display
				LCDState++;			
				LCDDlay = 5;		//  Wait 5 msecs for Clear to Complete
				break;
			case 10:			//  Wait for Command to Complete
				if (--LCDDlay == 0)
					LCDState++;
				break;
			case 11:
				LCDByte(0x006, 0);	//  Step 9 - Shift 
				LCDState++;			
				break;
			case 12:
				LCDByte(0x00E, 0);	//  Step 10 - Display On
				LCDState = 0;		//  Ready to Run
				break;

			case 100:				//  Output a Character in "MessageOut"
				switch (temp = MessageOut[MessageOuti++]) {
					case '\0':		//  At the End of the Message
						LCDState = 0;
						MessageOuti = 0;	//  Reset Message Index
						break;
					case '\f':		//  Clear Display
						LCDByte(0x001, 0);
						LCDState++;
						LCDDlay = 5;
						break;
					case 254:		//  Command Byte follows
						if ((temp = MessageOut[MessageOuti++]) == 0)
							LCDState = 0;
						else {
							if (temp < 4) {
								LCDState++;
								LCDDlay = 5;
							}  //  endif
							LCDByte(temp, 0);
						}  //  endif
						break;
					default:		//  All other characters
						LCDByte(temp, 1);
				}  //  endswitch
				break;
			case 101:				//  Message Delay
				if (--LCDDlay == 0)
					LCDState--;
				break;
		}  //  endswitch

	}  //  endif

//  Put Different Interrupt Handlers here

}  //  End Interrupt Handler


//  Mainline
void main(void)					//  Template Mainline
{

	OPTION = 0x0D1;				//  Assign Prescaler to TMR0
						//   Prescaler is /4
	TMR0 = 0;				//  Reset the Timer for Start
	T0IE = 1;				//  Enable Timer Interrupts
	GIE = 1;				//  Enable Interrupts

	LCDInit();				//  Initialize the LCD Port

//  Put in Interface initialization code here

	TMR1H = TMR1L = 0;		//  Initialize Timer 1
	T1CON = 0x003;			//  1:1 Prescaler
						//  T1OSCEN Off
						//  _T1SYNC Active
						//  TMR1CS is Exteral Clock
						//  TMR1ON

	while (1 == 1) {			//  Loop forever

//  Put in Robot high level operation code here

		while (soundState != 2);	//  Wait for Sound Received

		CurrentTMR1 = (TMR1H * 0x0100) + TMR1L;

		Message1[13] = GetHex((CurrentTMR1 & 0x0F000) >> 12);
		Message1[14] = GetHex((CurrentTMR1 & 0x0F00) >> 8);
		Message1[15] = GetHex((CurrentTMR1 & 0x0F0) >> 4);
		Message1[16] = GetHex(CurrentTMR1 & 0x0F);

		LCDOut(Message1);		//  Indicate "Sound"

		Dlay(1000);			//  Wait 1 Second

		LCDOut(Message2);		//  Clear the Sound Message

		soundState = 0;		//  Reset and wait for next sound

	}  //  endwhile

}  //  End of Mainline